package com.iotechn.unimall.app.api.running;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.iotechn.unimall.biz.service.user.UserBizService;
import com.iotechn.unimall.core.Const;
import com.iotechn.unimall.core.exception.AppServiceException;
import com.iotechn.unimall.core.exception.ExceptionDefinition;
import com.iotechn.unimall.core.exception.ServiceException;
import com.iotechn.unimall.core.util.GeneratorUtil;
import com.iotechn.unimall.core.util.SHAUtil;
import com.iotechn.unimall.data.component.CacheComponent;
import com.iotechn.unimall.data.domain.RunningGroupDO;
import com.iotechn.unimall.data.domain.RunningGroupUserDO;
import com.iotechn.unimall.data.domain.UserDO;
import com.iotechn.unimall.data.dto.UserDTO;
import com.iotechn.unimall.data.enums.UserLevelType;
import com.iotechn.unimall.data.enums.UserLoginType;
import com.iotechn.unimall.data.mapper.*;
import com.iotechn.unimall.data.notify.SMSClient;
import com.iotechn.unimall.data.util.SessionUtil;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * Created by rize on 2019/6/30.
 */
@Service
@Slf4j
public class RunningUserServiceImpl implements RunningUserService {

    private static final String VERIFY_CODE_PREFIX = "VERIFY_CODE_";

    private static final Logger logger = LoggerFactory.getLogger(RunningUserServiceImpl.class);

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private SMSClient smsClient;

    @Autowired
    private CacheComponent cacheComponent;

    @Autowired
    private UserBizService userBizService;

    @Autowired
    private StringRedisTemplate userRedisTemplate;

    @Autowired
    private RunningGroupMapper runningGroupMapper;

    @Autowired
    private RunningRecordMapper runningRecordMapper;

    @Autowired
    private RunningAdminApplyMapper runningAdminApplyMapper;

    @Autowired
    private RunningGroupUserMapper runningGroupUserMapper;


    private OkHttpClient okHttpClient = new OkHttpClient();

    @Value("${com.iotechn.unimall.wx.mini.app-id}")
    private String wxMiNiAppid;

    @Value("${com.iotechn.unimall.wx.mini.app-secret}")
    private String wxMiNiSecret;

    @Value("${com.iotechn.unimall.wx.app.app-id}")
    private String wxAppAppid;

    @Value("${com.iotechn.unimall.wx.app.app-secret}")
    private String wxAppSecret;

    @Value("${com.iotechn.unimall.wx.h5.app-id}")
    private String wxH5Appid;

    @Value("${com.iotechn.unimall.wx.h5.app-secret}")
    private String wxH5Secret;


    @Override
    public UserDTO thirdPartLogin(Integer loginType, String ip, String raw) throws ServiceException {
        try {
            if (UserLoginType.MP_WEIXIN.getCode() == loginType) {
                return wechatLogin(loginType, ip, raw);
            } else if (UserLoginType.H5_WEIXIN.getCode() == loginType) {
                //H5 微信公众号网页登录
                String json = okHttpClient.newCall(
                        new Request.Builder().url("https://api.weixin.qq.com/sns/oauth2/access_token?appid="
                        + wxH5Appid + "&secret=" + wxH5Secret + "&code=" + raw + "&grant_type=authorization_code").build()).execute().body().string();
                JSONObject jsonObject = JSONObject.parseObject(json);
                Integer errcode = jsonObject.getInteger("errcode");
                if (errcode == null || errcode == 0) {
                    String openid = jsonObject.getString("openid");
                    List<UserDO> userDOS = userMapper.selectList(new QueryWrapper<UserDO>().eq("open_id", openid).eq("login_type", loginType));
                    if (!CollectionUtils.isEmpty(userDOS)) {
                        //若用户已经注册，则直接返回用户
                        String accessToken = GeneratorUtil.genSessionId();
                        UserDTO userDTO = new UserDTO();
                        BeanUtils.copyProperties(userDOS.get(0), userDTO);
                        userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(userDTO));
                        userRedisTemplate.opsForValue().set("USER_TOKEN_" + userDTO.getId(),accessToken);
                        userDTO.setAccessToken(accessToken);
                        return userDTO;
                    } else {
                        String userAccessToken = jsonObject.getString("access_token");
                        //通过用户AccessToken换取用户信息
                        String userInfoJson = okHttpClient.newCall(
                                new Request.Builder().url("https://api.weixin.qq.com/sns/userinfo?access_token="
                                        + userAccessToken + "&openid=" + openid + "&lang=zh_CN").build()).execute().body().string();
                        JSONObject userInfoJsonObject = JSONObject.parseObject(userInfoJson);
                        Date now = new Date();
                        UserDO newUserDO = new UserDO();
                        newUserDO.setLoginType(loginType);
                        newUserDO.setNickname(userInfoJsonObject.getString("nickname"));
                        newUserDO.setAvatarUrl(userInfoJsonObject.getString("headimgurl"));
                        newUserDO.setGender(userInfoJsonObject.getInteger("sex"));
                        newUserDO.setOpenId(openid);
                        newUserDO.setLastLoginIp(ip);
                        newUserDO.setGmtLastLogin(now);
                        newUserDO.setGmtUpdate(now);
                        newUserDO.setGmtCreate(now);
                        userMapper.insert(newUserDO);
                        //这一步是为了封装上数据库上配置的默认值
                        UserDO userDO = userMapper.selectById(newUserDO.getId());
                        String accessToken = GeneratorUtil.genSessionId();
                        UserDTO userDTO = new UserDTO();
                        BeanUtils.copyProperties(userDO, userDTO);
                        userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(userDTO));
                        userRedisTemplate.opsForValue().set("USER_TOKEN_" + userDTO.getId(),accessToken);
                        userDTO.setAccessToken(accessToken);
                        return userDTO;
                    }
                } else {
                    throw new AppServiceException(ExceptionDefinition.USER_THIRD_PART_LOGIN_FAILED);
                }
            } else if (UserLoginType.APP_WEIXIN.getCode() == loginType) {
                //return wechatLogin(loginType, ip, raw);
                //UNI-APP 的 微信APP登录 APPSecret是保存在前端的。这点非常不安全。但是用了他的框架，也没有办法
                JSONObject jsonObject = JSONObject.parseObject(raw);
                JSONObject authResult = jsonObject.getJSONObject("authResult");
                String openid = authResult.getString("openid");
//                String openid = "osTQe6L_JOoJkrMO1vAT_peMivjA";
                List<UserDO> userDOS = userMapper.selectList(new QueryWrapper<UserDO>().eq("open_id", openid).eq("login_type", loginType));
                UserDO userDO;
                if (CollectionUtils.isEmpty(userDOS)) {
                    //创建新用户
                    Date now = new Date();
                    UserDO newUserDO = new UserDO();
                    newUserDO.setLoginType(loginType);
                    newUserDO.setOpenId(openid);
                    newUserDO.setLastLoginIp(ip);
                    newUserDO.setGmtLastLogin(now);
                    newUserDO.setGmtUpdate(now);
                    newUserDO.setGmtCreate(now);
                    userMapper.insert(newUserDO);
                    //这一步是为了封装上数据库上配置的默认值
                    userDO = userMapper.selectById(newUserDO.getId());
                } else {
                    userDO = userDOS.get(0);
                    UserDO userUpdateDO = new UserDO();
                    userUpdateDO.setId(userDO.getId());
                    userUpdateDO.setGmtLastLogin(new Date());
                    userUpdateDO.setLastLoginIp(ip);
                    userMapper.updateById(userUpdateDO);
                }
                //检查帐号是否已经冻结
                if (userDO.getStatus() == 0) {
                    throw new AppServiceException(ExceptionDefinition.USER_CAN_NOT_ACTICE);
                }
                String accessToken = GeneratorUtil.genSessionId();
                UserDTO userDTO = new UserDTO();
                BeanUtils.copyProperties(userDO, userDTO);
                userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(userDTO));
                userRedisTemplate.opsForValue().set("USER_TOKEN_" + userDTO.getId(),accessToken);
                userDTO.setAccessToken(accessToken);
                return userDTO;
            } else {
                throw new AppServiceException(ExceptionDefinition.USER_THIRD_PART_NOT_SUPPORT);
            }
        } catch (ServiceException e) {
            throw e;
        } catch (Exception e) {
            logger.error("[用户第三方登录] 异常", e);
            throw new AppServiceException(ExceptionDefinition.USER_THIRD_PART_LOGIN_FAILED);
        }
    }

    /**
     * 微信第三方登录 抽取接口(目前只有小程序)
     *
     * @param loginType
     * @param ip
     * @param raw
     * @return
     * @throws Exception
     */
    private UserDTO wechatLogin(Integer loginType, String ip, String raw) throws Exception {
        //微信第三方登录
        JSONObject thirdPartJsonObject = JSONObject.parseObject(raw);
        String code = thirdPartJsonObject.getString("code");
        String body = okHttpClient.newCall(new Request.Builder()
                .url("https://api.weixin.qq.com/sns/jscode2session?appid=" + (UserLoginType.MP_WEIXIN.getCode() == loginType ? wxMiNiAppid : wxAppAppid) +
                        "&secret=" + (UserLoginType.MP_WEIXIN.getCode() == loginType ? wxMiNiSecret : wxAppSecret) +
                        "&grant_type=authorization_code&js_code=" + code).get().build()).execute().body().string();
        JSONObject jsonObject = JSONObject.parseObject(body);
        System.out.println("微信解密后的报文");
        Integer errcode = jsonObject.getInteger("errcode");
        if (errcode == null || errcode == 0) {
            String miniOpenId = jsonObject.getString("openid");
            List<UserDO> userDOS = userMapper.selectList(new QueryWrapper<UserDO>().eq("open_id", miniOpenId).eq("login_type", loginType));
            UserDO userDO;
            if (CollectionUtils.isEmpty(userDOS)) {
                //若用户为空，则注册此用户
                Date now = new Date();
                UserDO newUserDO = new UserDO();
                newUserDO.setLoginType(loginType);
                newUserDO.setOpenId(miniOpenId);
                newUserDO.setLastLoginIp(ip);
                newUserDO.setGmtLastLogin(now);
                newUserDO.setGmtUpdate(now);
                newUserDO.setGmtCreate(now);
                userMapper.insert(newUserDO);
                //这一步是为了封装上数据库上配置的默认值
                userDO = userMapper.selectById(newUserDO.getId());
            } else {
                userDO = userDOS.get(0);
                UserDO userUpdateDO = new UserDO();
                userUpdateDO.setId(userDO.getId());
                userUpdateDO.setGmtLastLogin(new Date());
                userUpdateDO.setLastLoginIp(ip);
                userMapper.updateById(userUpdateDO);
            }
            //检查帐号是否已经冻结
            if (userDO.getStatus() == 0) {
                throw new AppServiceException(ExceptionDefinition.USER_CAN_NOT_ACTICE);
            }
            String accessToken = GeneratorUtil.genSessionId();
            UserDTO userDTO = new UserDTO();
            BeanUtils.copyProperties(userDO, userDTO);
            userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(userDTO));
            userRedisTemplate.opsForValue().set("USER_TOKEN_" + userDTO.getId(),accessToken);
            userDTO.setAccessToken(accessToken);

            //查询所属跑步组名称
            if(userDO.getLevel()!=null&&userDO.getLevel()>0){
                Long runningGroupId = userDO.getRunningGroupId();
                RunningGroupDO runningGroupDO = runningGroupMapper.selectById(runningGroupId);
                if(runningGroupDO!=null){
                    userDTO.setRunningGroupId(runningGroupDO.getId());
                    userDTO.setRunningGroupName(runningGroupDO.getGroupName());
                }
            }

            return userDTO;
        } else {
            throw new AppServiceException(ExceptionDefinition.USER_THIRD_UNEXPECT_RESPONSE);
        }

    }

    @Override
    public String syncUserInfo(String nickName, String nickname, String avatarUrl, Integer gender, Long birthday, String accessToken, Long userId) throws ServiceException {
        UserDO updateUserDO = new UserDO();
        updateUserDO.setId(userId);
        updateUserDO.setNickname(StringUtils.isEmpty(nickName) ? nickname: nickName);
        updateUserDO.setAvatarUrl(avatarUrl);
        updateUserDO.setGender(gender);
        updateUserDO.setGmtUpdate(new Date());
        if (birthday != null) {
            updateUserDO.setBirthday(new Date(birthday));
        }
        if (userMapper.updateById(updateUserDO) > 0) {
            //更新SESSION缓存
            UserDTO user = SessionUtil.getUser();
            if (!StringUtils.isEmpty(nickName)) {
                user.setNickname(nickName);
            }
            if (!StringUtils.isEmpty(avatarUrl)) {
                user.setAvatarUrl(avatarUrl);
            }
            if (birthday != null) {
                user.setBirthday(new Date(birthday));
            }
            if (gender != null) {
                user.setGender(gender);
            }
            userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(user));
            userRedisTemplate.opsForValue().set("USER_TOKEN_" + userId,accessToken);
            return "ok";
        }
        throw new AppServiceException(ExceptionDefinition.USER_UNKNOWN_EXCEPTION);
    }

    @Override
    public UserDTO getUserInfo(Long userId) throws ServiceException {
        UserDO userDO = userMapper.selectById(userId);
        if(userDO==null){
            throw new AppServiceException("用户尚未登录",10001);
        }
        UserDTO userDTO = new UserDTO();
        BeanUtils.copyProperties(userDO, userDTO);
        //查询所属跑步组名称
        if(userDO.getLevel()!=null&&userDO.getLevel()>0){
            Long runningGroupId = userDO.getRunningGroupId();
            RunningGroupDO runningGroupDO = runningGroupMapper.selectById(runningGroupId);
            if(runningGroupDO!=null){
                userDTO.setRunningGroupId(runningGroupDO.getId());
                userDTO.setRunningGroupName(runningGroupDO.getGroupName());
            }
            //用户等级取用户所在跑团的等级
            RunningGroupUserDO runningGroupUserDO = runningGroupUserMapper.selectByGroupIdAndUserId(runningGroupId, userId);
            if(runningGroupUserDO!=null){
                userDTO.setLevel(runningGroupUserDO.getLevel()<1?1:runningGroupUserDO.getLevel());
            }

        }
        return userDTO;
    }

    @Override
    public Object getH5Sign(String url) throws ServiceException {
        try {
            String wxH5AccessToken = userBizService.getWxH5AccessToken();
            //我也不知道为什么微信这里要换两次
            String wxH5Ticket = userBizService.getWxH5Ticket(wxH5AccessToken);
            String noncestr = GeneratorUtil.genUUId();
            long timestamp = System.currentTimeMillis();
            StringBuilder sb = new StringBuilder();
            sb.append("jsapi_ticket=");
            sb.append(wxH5Ticket);
            sb.append("&noncestr=");
            sb.append(noncestr);
            sb.append("&timestamp=");
            sb.append(timestamp);
            sb.append("&url=");
            sb.append(url);
            //明文
            String content = sb.toString();
            String signature = SHAUtil.sha256Encode(content);
            Map<String, Object> obj = new HashMap<>();
            obj.put("noncestr", noncestr);
            obj.put("timestamp", timestamp);
            obj.put("sign", signature);
            return obj;
        } catch (Exception e) {
            logger.info("[获取H5签名] 异常", e);
            throw new AppServiceException(ExceptionDefinition.APP_UNKNOWN_EXCEPTION);
        }
    }

    @Override
    public Page<UserDTO> selectAdminPage(Long userId, Integer pageNo, Integer pageSize) {
        Long runningGroupId = -1L;
        UserDO userDO1 = userMapper.selectById(userId);
        if(userDO1!=null){
            runningGroupId = userDO1.getRunningGroupId();
        }
        Page page=new Page<>(pageNo,pageSize);
//        List<Integer> levelList=new ArrayList<>();
//        levelList.add(UserLevelType.ADMIN.getCode());
//        levelList.add(UserLevelType.SUPER_VIP.getCode());

//        Page<UserDO> selectPage = userMapper.selectPage(page, new QueryWrapper<UserDO>()
//                .in("level", levelList).eq("running_group_id",userDO1.getRunningGroupId())
//                .eq("running_group_id",runningGroupId)
//               );
//        List<UserDO> list = selectPage.getRecords();

        List<UserDO> list = runningGroupUserMapper.selectAdminPage(page, runningGroupId);

        List<UserDTO> dtoList=new ArrayList<>();
        for(UserDO userDO:list){
            UserDTO dto=new UserDTO();
            BeanUtils.copyProperties(userDO, dto);

            dtoList.add(dto);
        }
        page.setRecords(dtoList);
        return page;
    }

    @Override
    public Boolean cancelAdmin(Long recordId, Long userId) throws ServiceException {
//        UserDO update=new UserDO();
//        update.setId(recordId);
//        update.setLevel(UserLevelType.VIP.getCode());
//        update.setGmtUpdate(new Date());
//        userMapper.updateById(update);

        UserDO userDO = userMapper.selectById(recordId);
        if(userDO!=null){
            Long runningGroupId = userDO.getRunningGroupId();
            RunningGroupUserDO runningGroupUserDO = runningGroupUserMapper.selectByGroupIdAndUserId(runningGroupId, recordId);
            if(runningGroupUserDO!=null){
                RunningGroupUserDO updateGroupUser =new RunningGroupUserDO();
                updateGroupUser.setId(runningGroupUserDO.getId());
                updateGroupUser.setLevel(UserLevelType.VIP.getCode());
                updateGroupUser.setGmtUpdate(new Date());
                runningGroupUserMapper.updateById(updateGroupUser);
            }
        }

        return true;
    }

    @Override
    public Boolean deleteUser(Long recordId, Long userId) throws ServiceException {
        UserDO userDO = (UserDO)this.userMapper.selectById(recordId);
        if (userDO == null) {
            throw new AppServiceException("没有获取到有效用户", 201);
        }
        Long runningGroupId = userDO.getRunningGroupId();
        RunningGroupUserDO runningGroupUserDO = runningGroupUserMapper.selectByGroupIdAndUserId(runningGroupId, recordId);

        Integer level = runningGroupUserDO.getLevel();
        if (level == null) {
            level = Integer.valueOf(0);
        }
        if (level.intValue() > 1) {
            throw new AppServiceException("该用户是管理员，请先删除他的管理员角色", 201);
        }
//        userMapper.deleteById(recordId);
//        userMapper.removeRunningUser(recordId);
//        runningRecordMapper.deleteByUserId(recordId);
//        runningAdminApplyMapper.deleteByUserId(recordId);

        runningRecordMapper.deleteByUserIdAndGroupId(recordId,runningGroupId);
        runningAdminApplyMapper.deleteByUserIdAndGroupId(recordId,runningGroupId);
        //把用户移除当前跑团
        this.removeUserGroup(runningGroupId,recordId);

        String accessToken = (String)this.userRedisTemplate.opsForValue().get("USER_TOKEN_" + recordId);
        this.userRedisTemplate.delete(Const.USER_REDIS_PREFIX + accessToken);

        return Boolean.valueOf(true);
    }

    @Override
    public Page<UserDO> selectUncommitUser(Long userId, Integer pageNo, Integer pageSize) {
        Long runningGroupId = -1L;
        UserDO userDO = userMapper.selectById(userId);
        if(userDO!=null){
            runningGroupId = userDO.getRunningGroupId();
        }

        Page<UserDO> page = new Page(pageNo.intValue(), pageSize.intValue());
        List<UserDO> dtoList = this.userMapper.selectselectUncommitPage(page,runningGroupId);
        page.setRecords(dtoList);
        return page;
    }

    @Override
    public List<RunningGroupUserDO> getUserGroup(Long userId) {
        List<RunningGroupUserDO> runningGroupUserDOS = runningGroupUserMapper.selectByUserId(userId);
        return runningGroupUserDOS;
    }

    @Override
    public Boolean removeUserGroup(Long runningGroupId, Long userId) throws ServiceException {
        int i = runningGroupUserMapper.deleteByGroupIdAndUserId(runningGroupId, userId);
        UserDO userDO = userMapper.selectById(userId);
        //如果移除的是当前用户的跑团，那么把移除后剩下的跑团默认为他的跑团
        if(userDO!=null&&userDO.getRunningGroupId()!=null&&userDO.getRunningGroupId().equals(runningGroupId)){
            List<RunningGroupUserDO> userGroupList = this.getUserGroup(userId);
            if(CollectionUtil.isNotEmpty(userGroupList)){
                UserDO update =new UserDO();
                update.setId(userDO.getId());
                update.setRunningGroupId(userGroupList.get(0).getRunningGroupId());
                update.setGmtUpdate(new Date());
                userMapper.updateById(update);
            }else{
                //如果移除后没有跑团了，
                UserDO update=new UserDO();
                update.setId(userId);
                update.setLevel(UserLevelType.COMMON.getCode());
                update.setRunningGroupId(null);
                update.setGmtUpdate(new Date());
                userMapper.updateById(update);
            }
        }

        return true;
    }

    @Override
    public Boolean setCurrentUserGroup(Long runningGroupId, Long userId) throws ServiceException {
        UserDO userUpdateDO = new UserDO();
        userUpdateDO.setId(userId);
        userUpdateDO.setRunningGroupId(runningGroupId);
        userMapper.updateById(userUpdateDO);

        return Boolean.TRUE;
    }


}
